#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>
#include <unordered_map>

using namespace std;

namespace http_ns
{

    const string sep = "\r\n";
    const string headsep = ": ";
    const string firstpage = "index.html";
    const string space = " ";
    const string filesuffixsep = ".";


    //----------------------------------------------------
    class Request
    {
    private:
        // 请求行: 请求方法 空格 URI 空格 HTTP版本 换行符
        // 请求报头: key:空格value
        // 空行
        // 正文

        string ReqLine;
        vector<string> ReqHead;
        string BlockLine;
        string Text;

        string Method;
        string URI;
        string Version;
        unordered_map<string, string> KeyVal;

        string resource_path = "wwwroot";
        string suffix = ".html";

    public:
        Request(string Block = sep) 
            :BlockLine(Block)
        {}
        ~Request() {}

        void Serislize()
        {
        }

        string GetLine(string &request)
        {
            if (request.empty())
                return "";

            int pos = request.find(sep);
            if (pos == string::npos)
                return "";

            string ret = request.substr(0, pos);
            request.erase(0, pos + sep.size());

            return ret.empty() ? sep : ret;
        }

        void GetKeyVal(string &req, string &key, string &val)
        {
            int pos = req.find(headsep);
            key = req.substr(0, pos);
            val = req.substr(pos + headsep.size());
        }

        void DeSerislize(string &request) // 将请求反序列化
        {
            cout << request << endl;
            ReqLine = GetLine(request); // 得到请求行

            while (true)
            {
                string temp = GetLine(request);
                if (temp == sep)
                {
                    Text = request;
                    break;
                }
                else if (temp.empty())
                {
                    break;
                }
                else
                {
                    ReqHead.emplace_back(temp);
                }
            }

            stringstream ss(ReqLine);
            ss >> Method >> URI >> Version;

            resource_path += URI;
            if (resource_path[resource_path.size() - 1] == '/')
                resource_path += firstpage;

            int pos = resource_path.rfind(filesuffixsep);
            if(pos != string::npos)
            {
                suffix = resource_path.substr(pos);
            }

            for (auto &e : ReqHead)
            {
                string key, val;
                GetKeyVal(e, key, val);

                KeyVal.insert(make_pair(key, val));
            }
        }

        void Print()
        {
            std::cout << "===" << ReqLine << std::endl;
            for (auto &header : ReqHead)
            {
                std::cout << "***" << header << std::endl;
            }
            std::cout << BlockLine;
            std::cout << Text << std::endl;

            std::cout << "method ### " << Method << std::endl;
            std::cout << "url ### " << URI << std::endl;
            std::cout << "path ### " << resource_path << std::endl;
            std::cout << "httpverion ### " << Version << std::endl;
            for (auto &header : KeyVal)
            {
                std::cout << "@@@" << header.first << " - " << header.second << std::endl;
            }
        }

        string GetPath()
        {
            return resource_path;
        }

        string GetSuffix()
        {
            return suffix;
        }
    };


    //----------------------------------------------------
    const string version = "HTTP/1.0";


    //----------------------------------------------------
    class Response
    {
    private:
        // 状态行: HTTP版本 空格 状态符 空格 状态符描述 空格换行符
        // 应答报头: key:空格value
        // 空行
        // 正文

        string StatusLine;
        vector<string> RespHead;
        string BlockLine;
        string Text;

        string Version = version;
        int code;
        string desc;
        unordered_map<string, string> KeyVal;

    public:
        Response(string httpversion = version, string Block = sep)
            : Version(httpversion)
            , BlockLine(Block)
        {}
        ~Response() {}

        void AddCode(int codenum)
        {
            code = codenum;
            desc = "OK"; //TODO
        }

        void AddKeyVal(string key, string val)
        {
            KeyVal[key] = val;
        }

        void AddText(string text)
        {
            Text = text;
        }

        string Serislize()
        {
            StatusLine = Version + space + to_string(code) + space + desc + sep; //状态行

            for(auto &e : KeyVal)
            {
                RespHead.emplace_back(e.first + headsep + e.second + sep);
            }

            string ret = StatusLine;
            for(auto &e: RespHead)
            {
                ret += e;
            }

            ret += BlockLine + Text;
            return ret;
        }

        void DeSerislize()
        {
        }
    };


    //----------------------------------------------------
    class HTTP
    {

    private:
        unordered_map<string, string> mini_type; //文件后缀对应的Content-Type
        unordered_map<int, string> status_code;  //状态码对应的描述

    public:
        HTTP() {}
        ~HTTP() {}

        string GetText(string path, int& size)
        {
            ifstream in(path, ios::binary);
            if(!in.is_open()) return "";

            in.seekg(0, ios::end);
            size = in.tellg();
            in.seekg(0, ios::beg);

            string text; 
            text.resize(size);
            in.read((char*)text.c_str(), size);
            in.close();

            return text;
        }

        string deal(string &request) // 收到请求
        {
            if (request.empty())
                return "";

            shared_ptr<Request> req_ptr = make_shared<Request>();
            req_ptr->DeSerislize(request);
            // req_ptr->Print();
            
            int size = 0;
            string text = GetText(req_ptr->GetPath(), size);

            shared_ptr<Response> resp_ptr = make_shared<Response>();
            resp_ptr->AddCode(200);
            resp_ptr->AddText(text);
            resp_ptr->AddKeyVal("Content-Length", to_string(size));
            resp_ptr->AddKeyVal("Content-Type", mini_type[req_ptr->GetSuffix()]);

            return resp_ptr->Serislize();
        }
    };

}
